package glassline;

import glassline.interfaces.Conveyor;
import glassline.interfaces.Sensor;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import transducer.TChannel;
import transducer.TEvent;

public class ConveyorAgent extends FactoryAgent implements Conveyor {

	boolean manualOverride = false;
	public boolean sentAlertGlassBlockingEntry = false;

	public List<Glass> myGlass = Collections.synchronizedList(new ArrayList<Glass>());
	
	public class ConveyorSensor {
		public Sensor sensor; //All instances related to the Sensor should use the interface, not the agent, for proper unit testing
		public boolean isSensing = false;
		
		public ConveyorSensor(Sensor sensor) {
			this.sensor = sensor;
		}
	}
	
	ConveyorSensor beginningSensor;
	ConveyorSensor endSensor;
	public List<ConveyorSensor> sensorHits = Collections.synchronizedList(new ArrayList<ConveyorSensor>()); // List of unprocessed sensor state changes;
	
	public ConveyorAgent(int conveyorID) {
		super(conveyorID);
		start();
	}
	
	//Messages
	/* (non-Javadoc)
	 * @see glassline.Conveyor#msgSensedGlass(sun.management.Sensor, boolean)
	 */
	public void msgSensedGlass(Sensor sensor, boolean sensing) {
		//print("received message that " + sensor + " sensing is " + sensing);
		
		ConveyorSensor sender = null;
		if (beginningSensor.sensor == sensor) {
			sender = beginningSensor;
		} else if (endSensor.sensor == sensor) {
			sender = endSensor;
		} else return;
	
		sender.isSensing = sensing;
		sensorHits.add(sender);
		stateChanged();
	}
	
	/* (non-Javadoc)
	 * @see glassline.Conveyor#msgHereIsGlass(glassline.Glass)
	 */
	public void msgHereIsGlass(Glass g) {
		//print("Received " + g);
		myGlass.add(g);
		stateChanged();
	}
	
	//scheduler
	public boolean pickAndExecuteAnAction() {
		
		// Rule: Prevents collisions by moving a glass onto a stopped conveyor with glass at the beginning.
		if (on && !nextSpaceAvailable()) {
			if (endSensor.isSensing) {
				stop();
				return true;
			}
		}
		
		// Rule: Restarts conveyor if nothing is blocking the glass.
		if (!on && nextSpaceAvailable()) {
			start();
			return true;
		}
	
		// Rule: Process a sensor state change.
		if (!sensorHits.isEmpty()) {
			ConveyorSensor sensor = sensorHits.remove(0);
				if (sensor == endSensor) {
					if (myGlass.isEmpty()) {
						Do("*****************Error: The end sensor was tripped on conveyor " + componentNumber + " with an empty myGlass list");
					}
					
					if (sensor.isSensing) { // Glass just triggered sensor;
						return true;
					}
					else { // Glass just left sensor
						if (!on) {
							Do("Error: glass left last sensor but I'm off!");
						}
						passGlassForward();
						return true;
					}
				}
			if (sensor == beginningSensor) {
				if (sensor.isSensing) { // Glass just triggered sensor
					if (!on && !sentAlertGlassBlockingEntry) {
						alertPreviousComponent();
						return true;
					} else {
						return true;
					}
				}
				else { // Glass just left sensor
					return true;
				}
			}
		}
		return false;
	}
	
	//Actions
	protected void passGlassForward() {
		Glass g = myGlass.get(0);
		Do("passing " + g + " to " + nextComponent.component);
		if (nextComponent != null) {
			nextComponent.component.msgHereIsGlass(g);
		}
		myGlass.remove(0);
		stateChanged();
	}
	
	protected void alertPreviousComponent() {
		Do("telling " + previousComponent + " there is no space available");
		previousComponent.msgIHaveSpaceAvailable(false);
		sentAlertGlassBlockingEntry = true;
		stateChanged();
	}
	
	protected void start() {
		on = true;
		if (sentAlertGlassBlockingEntry) {
			previousComponent.msgIHaveSpaceAvailable(true);
		}
		sentAlertGlassBlockingEntry = false;
		Do("Start conveyor");
		Object[] args = {componentNumber};
		Factory.transducer.fireEvent(TChannel.CONVEYOR, TEvent.CONVEYOR_DO_START, args);
		stateChanged();
	}
	
	protected void stop() {
		on = false;
		Do("Stop Conveyor");
		
		// Tell the previous component that there is no space for a glass to move onto this conveyor.
		// This handles the case where the conveyor is stopped with glass on the beginning sensor.
		if (beginningSensor.isSensing) {
			Do("telling " + previousComponent + " there is no space available");
			sentAlertGlassBlockingEntry = true;
			previousComponent.msgIHaveSpaceAvailable(false);
		}
		
		// Tell GUI to stop
		Object[] args = {componentNumber};
		Factory.transducer.fireEvent(TChannel.CONVEYOR, TEvent.CONVEYOR_DO_STOP, args);
		stateChanged();
	}
	
	//Utilities	
	public void setBeginningSensor(Sensor sensor) {
		beginningSensor = new ConveyorSensor(sensor);
	}
	
	public void setEndSensor(Sensor sensor) {
		endSensor = new ConveyorSensor(sensor);
	}

	public String toString() {
		return "Conveyor " + componentNumber;
	}
}